package com.example.demo.factory

import com.example.demo.anno.RouteMapping
import com.example.demo.enum.RouteMethod
import com.example.demo.expansion.coroutineHandler
import com.example.demo.singleton.VertxHolder
import com.example.demo.util.getReflections
import io.vertx.core.Vertx
import io.vertx.core.json.JsonObject
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.BodyHandler
import org.reflections.Reflections
import org.slf4j.LoggerFactory

/**
 * @author Xu Haidong
 * @date 2023/4/21 10:18
 */
private val logger = LoggerFactory.getLogger("routeFactory")

fun createRouter(vertx: Vertx): Router {
  val reflections: Reflections = getReflections(findRouteLocation(vertx))
  val routeClass = reflections.getTypesAnnotatedWith(RouteMapping::class.java)
  val router = Router.router(vertx)
  router.route().handler(BodyHandler.create())
    .failureHandler {
      logger.error(it.failure().message, it.failure())
      it.response().end(JsonObject().put("code", 500).encodePrettily())
    }
  routeClass.forEach {
    registerHandler(router, it)
  }
  return router
}

fun findRouteLocation(vertx: Vertx): String? {
  val sharedData = vertx.sharedData()
  val localMap = sharedData.getLocalMap<String, JsonObject>("demo")
  val config = localMap["config"]
  val locationConfig = config?.getJsonObject("location")
  logger.info("路由路径：$locationConfig")
  return locationConfig?.getString("route")
}

fun registerHandler(router: Router, handlerClass: Class<*>) {
  val rm = handlerClass.getAnnotation(RouteMapping::class.java)
  //route顶层路径
  val rootPath = if (rm.value.startsWith("/")) rm.value else "/" + rm.value
  val instance = handlerClass.newInstance()
  val methods = handlerClass.methods
  methods.filter { it.isAnnotationPresent(RouteMapping::class.java) }.forEach { method ->
    val mapping = method.getAnnotation(RouteMapping::class.java)
    val p = mapping.value
    val m = mapping.method
    val url = rootPath + (if (p.startsWith("/")) p else "/$p")
    logger.info("注册路由path=$url，method=$m")
    val r = method.invoke(instance) as suspend (RoutingContext) -> Unit
    when (m) {
      RouteMethod.GET -> router.get(url).coroutineHandler(r)
      RouteMethod.POST -> router.post(url).coroutineHandler(r)
    }
  }
}
